﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Packaging;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using VsxFactory.Modeling.Strategies;
using VsxFactory.Modeling.VisualStudio;

namespace VsxFactory.Modeling.Repository
{
    /// <summary>
    /// 
    /// </summary>
    public static class PackageHelper
    {
        /// <summary>
        /// Manifest file name (same for all packages)
        /// </summary>
        public static readonly string MANIFEST_FILE_NAME = "package.manifest";
        /// <summary>
        /// Package extension
        /// </summary>
        public static readonly string PACKAGE_EXTENSION = ".pkx";

        public static IEnumerable<PackageManifest> EnumeratePackages(IStrategyContext context, string kind)
        {
            string folder = context.Settings.GetPackageFolder(kind);

            foreach (var modelFile in Utils.SearchFile(folder, "*" + PACKAGE_EXTENSION))
            {
                PackageManifest manifest = null;
                try
                {
                    manifest = GetManifestFromPackage(modelFile);
                }
                catch (Exception ex)
                {
                    context.WriteError("Error when reading manifest from package {0} - {1}", modelFile, ex.Message);
                }
                if (manifest != null)                
                    yield return manifest;                
            }
        }

        public static string SaveManifest(PackageManifest manifest, string manifestFileName)
        {
            Guard.ArgumentNotNull(manifest, "manifest");
            Guard.ArgumentNotNullOrEmptyString(manifestFileName, "manifestFileName");

            if(String.IsNullOrEmpty(manifest.Name) || String.IsNullOrEmpty(manifest.Version))
                throw new InvalidDataException("Invalid manifest");

            using(Stream writer = new FileStream(manifestFileName, FileMode.Create, FileAccess.Write))
            {
                using(XmlWriter xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))
                {
                    System.Xml.Serialization.XmlSerializer ser = new XmlSerializer(typeof(PackageManifest));
                    ser.Serialize(xmlWriter, manifest);
                }
            }
            return manifestFileName;
        }

        public static PackageManifest LoadManifest(string manifestFileName)
        {
            Guard.ArgumentNotNullOrEmptyString(manifestFileName, "manifestFileName");

            using(Stream reader = new FileStream(manifestFileName, FileMode.Open, FileAccess.Read))
            {
                return DeserializeManifest(reader);
            }
        }

        /// <summary>
        /// Gets the infos.
        /// </summary>
        /// <param name="packageFileName">Name of the package file.</param>
        /// <returns></returns>
        public static PackageManifest GetManifestFromPackage(string packageFileName)
        {
            Package package = null;
            try
            {
                try
                {
                    package = Package.Open(packageFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                }
                catch
                {
                    return null;
                }

                Uri manifestUri = new Uri("/" + MANIFEST_FILE_NAME, UriKind.Relative);
                if (!package.PartExists(manifestUri))
                {
                    return null;
                }

                try
                {
                    using (Stream reader = package.GetPart(manifestUri).GetStream(FileMode.Open, FileAccess.Read))
                    {
                        var manifest = DeserializeManifest(reader);
                        return manifest;
                    }
                }
                catch
                {
                    return null;
                }
            }
            finally
            {
                if (package != null)
                    package.Close();
            }
        }

        /// <summary>
        /// Deserializes the manifest.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        internal static PackageManifest DeserializeManifest(Stream reader)
        {
            Guard.ArgumentNotNull(reader, "reader");

            using (XmlReader xmlReader = XmlReader.Create(reader, new XmlReaderSettings {  IgnoreComments = true, IgnoreWhitespace = true }))
            {
                // TODO schema validation
                System.Xml.Serialization.XmlSerializer ser = new XmlSerializer(typeof(PackageManifest));
                PackageManifest manifest = (PackageManifest)ser.Deserialize(xmlReader);
                return manifest;
            }
        }
    }
}
